﻿//+-------------------------------------------------------------------------------------------------------------------------------------------------------
//
//Description: VmRoleResourceDefinitionViewModel
//Author: Aravind Ramarathinam (aravr@microsoft.com)
//
//Copyright © Microsoft Corporation
//Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
//You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 
//
//THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, 
//INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT. 
//See the Apache Version 2.0 License for specific language governing permissions and limitations under the License.
// 
//--------------------------------------------------------------------------------------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Runtime.Serialization.Json;
using System.Text;
using System.Windows;
using VmroleAuthor.Helpers;
using VmroleAuthor.Models;

namespace VmroleAuthor.ViewModels
{
    public class VmRoleResourceDefinitionViewModel : TreeViewItemViewModel, IDataErrorInfo, IProduceValidationResults, ICanBeRemovedFromTree, ISaveToDisk, IJsonOutput
    {
        private readonly ResourceDefinition resourceDefinition;   
        private readonly ParametersViewModel resourceDefinitionParametersViewModel;
        private readonly ResourceExtensionReferenceViewModel resourceExtensionReferenceViewModel;
        private readonly ScaleOutSettingsViewModel scaleOutSettingsViewModel;
        private readonly HardwareProfileViewModel hardwareProfileViewModel;
        private readonly StorageProfileViewModel storageProfileViewModel;
        private readonly NetworkProfileViewModel networkProfileViewModel;
        private readonly OperatingSystemProfileViewModel operatingSystemProfileViewModel;
        private readonly ViewDefViewModel viewDefViewModel;
        private readonly bool isNewResdefCreatedByTheTool;

        public const string DefaultDataPackageFolderName = "DataPackage";

        private static class PropertyNames
        {
            public const string Identity = "Identity";
            public const string Name = "Name";
            public const string Version = "Version";
            public const string Publisher = "Publisher";
            public const string SchemaVersion = "SchemaVersion";
            public const string Type = "Type";          
        }

        public VmRoleResourceDefinitionViewModel(ResourceDefinition resExtDefinition, ViewDefinition viewDefinition, ResourceDefinitionStorageInfo resourceDefinitionStorageInfo, ObservableCollection<TreeViewItemViewModel> rootTreeViewItemViewModels, bool isNewResdefCreatedByTheTool)
            : base(null, true)
        {
            this.resourceDefinition = resExtDefinition;
            this.ResourceDefinitionStorageInfo = resourceDefinitionStorageInfo;
            this.resourceDefinitionParametersViewModel = new ParametersViewModel(this.resourceDefinition.ResourceParameters, this);
            this.isNewResdefCreatedByTheTool = isNewResdefCreatedByTheTool;
            // In v1 only res ext reference is supported
            this.resourceExtensionReferenceViewModel = new ResourceExtensionReferenceViewModel(resourceDefinition.ResourceExtensionReferences, this, rootTreeViewItemViewModels);
            if (this.resourceDefinition.IntrinsicSettings == null)
            {
                this.resourceDefinition.IntrinsicSettings = new IntrinsicSettings();                
            }
            if (this.resourceDefinition.IntrinsicSettings.HardwareProfile == null)
            {
                this.resourceDefinition.IntrinsicSettings.HardwareProfile = new VMHardwareProfile();
            }
            this.hardwareProfileViewModel = new HardwareProfileViewModel(this.resourceDefinition.IntrinsicSettings.HardwareProfile, this);
            this.scaleOutSettingsViewModel = new ScaleOutSettingsViewModel(this.resourceDefinition.IntrinsicSettings.ScaleOutSettings, this);
            this.storageProfileViewModel = new StorageProfileViewModel(this.resourceDefinition.IntrinsicSettings.StorageProfile, this);
            this.operatingSystemProfileViewModel = new OperatingSystemProfileViewModel(this.resourceDefinition.IntrinsicSettings.OperatingSystemProfile, this); 
            this.networkProfileViewModel = new NetworkProfileViewModel(this.resourceDefinition.IntrinsicSettings.NetworkProfile, this);
      
            if (viewDefinition != null)
            {
                this.viewDefViewModel = new ViewDefViewModel(viewDefinition, resourceDefinitionStorageInfo, this);
            }                       
            this.ValidationResults = new ObservableCollection<CloudServiceModelValidationResult>();
        }

        public ResourceDefinitionStorageInfo ResourceDefinitionStorageInfo { get; private set; }

        public bool IsNewResdefCreatedByTheTool
        {
            get { return this.isNewResdefCreatedByTheTool; }
        }

        public  ResourceDefinition ResourceDefinition
        {
            get { return this.resourceDefinition; }
        }

        public string Identity
        {
            get
            {
                return "Resource Definition ( " + this.resourceDefinition.Name + ":" + this.resourceDefinition.Version + " : " + this.resourceDefinition.Publisher + " )";
            }
        }

        public string Name
        {
            get { return this.resourceDefinition.Name; }
            set
            {
                this.resourceDefinition.Name = value;
                this.OnPropertyChanged(PropertyNames.Name);
                this.OnPropertyChanged(PropertyNames.Identity);
            }
        }

        public string Version
        {
            get { return this.resourceDefinition.Version; }
            set
            {
                this.resourceDefinition.Version = value;
                this.OnPropertyChanged(PropertyNames.Version);
                this.OnPropertyChanged(PropertyNames.Identity);
            }
        }

        public string Publisher
        {
            get { return this.resourceDefinition.Publisher; }
            set 
            { 
                this.resourceDefinition.Publisher = value;
                if (this.viewDefViewModel != null)
                {
                    this.viewDefViewModel.PublisherLabel.PropertyValue = ViewDefViewModel.AutoLocalizeMoniker;
                }
                this.OnPropertyChanged(PropertyNames.Publisher);
                this.OnPropertyChanged(PropertyNames.Identity);
            }
        }

        public string SchemaVersion
        {
            get { return this.resourceDefinition.SchemaVersion; }
            set 
            { 
                this.resourceDefinition.SchemaVersion = value;
                this.OnPropertyChanged(PropertyNames.SchemaVersion);
            }
        }

        public string Type
        {
            get { return this.resourceDefinition.Type; }
            set
            {
                this.resourceDefinition.Type = value;
                this.OnPropertyChanged(PropertyNames.Type);
            }
        }

        public ViewDefViewModel ViewDefViewModel
        {
            get { return this.viewDefViewModel; }
        }      
       
    
        public ParametersViewModel ResourceDefinitionParametersViewModel
        {
            get { return this.resourceDefinitionParametersViewModel; }
        }

        public string PrettyJson
        {
            get { return this.GetPrettyJson(false); }
        }

        public string GetRawJson(bool popOnError)
        {
            try
            {                    
                DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(ResourceDefinition));
                using (MemoryStream ms = new MemoryStream())
                {
                    serializer.WriteObject(ms, this.ResourceDefinition);
                    return Encoding.Default.GetString(ms.ToArray());
                }                  
            }
            catch (Exception ex)
            {
                string errorText = string.Format(
                    "Unable to serialize {0}. Click Validate. Exception details : {1}",
                    this.Name ?? "Unspecified name",
                    ex.Message);
                if (popOnError)
                {
                    MessageBox.Show(errorText, "VmRole ResourceDefinition Json Serialization");
                }
                return errorText;
            }         
        }


        public string GetPrettyJson(bool popOnError)
        {
            return JsonHelper.PrettyPrentJson(this.GetRawJson(popOnError));           
        }


        public void SaveToDisk()
        {
            if (this.ResourceDefinitionStorageInfo != null)
            {                
                try
                {
                    if (this.ResourceDefinitionStorageInfo.IsResdefPkg && this.ResourceDefinitionStorageInfo.ViewDefFileName != null && this.viewDefViewModel != null)
                    {
                        this.viewDefViewModel.SaveToDisk();
                    }                 
                    using (StreamWriter outfile = new StreamWriter
                        (new FileStream(this.ResourceDefinitionStorageInfo.JsonFileName, FileMode.Create, FileAccess.ReadWrite), 
                        Encoding.UTF8))
                    {
                        outfile.Write(this.GetPrettyJson(true));
                    }

                    if (this.ResourceDefinitionStorageInfo.IsResdefPkg)
                    {
                        PackagingFormatHelper.CreatePackage(this.ResourceDefinitionStorageInfo.WorkingDir, this.ResourceDefinitionStorageInfo.FileToSavePath);
                    }                   
                }
                catch (Exception ex)
                {
                    MessageBox.Show(
                        string.Format(
                        "Unable to save {0}  package. File save path {1}. Exception details : {2}",
                        this.Name ?? "Unspecified name",
                        this.ResourceDefinitionStorageInfo.JsonFileName,
                        ex.Message), "Save VmRole ResourceDefinition");
                }
            }
            else
            {
                MessageBox.Show(
                       string.Format(
                       "Unable to save {0} json in package. Storage Info not set",
                       this.Name ?? "Unspecified name"), "Save VmRole Resource Definition");               
            }     
        }

        protected override void LoadChildren()
        {
           this.Children.Add(this.resourceExtensionReferenceViewModel);           
           this.Children.Add(this.resourceDefinitionParametersViewModel);
           this.Children.Add(this.scaleOutSettingsViewModel);
           this.Children.Add(this.hardwareProfileViewModel);
           this.Children.Add(this.storageProfileViewModel);
           this.Children.Add(this.networkProfileViewModel);
           this.Children.Add(this.operatingSystemProfileViewModel); 
           if (this.viewDefViewModel != null)
           {
               this.Children.Add(this.viewDefViewModel);
           }
        }

        public void Cleanup()
        {
            //By design. The currently saved resext is not impacted.
        }
      
        public string this[string propertyName]
        {
            get
            {
                string error = null;
                switch (propertyName)
                {
                    case PropertyNames.Name:
                        error = ParameterizationHelper.ValidateCannotBeParameterizedBySpecification(this.Name);
                        break;
                    case PropertyNames.Version:
                        error = ValidationHelper.ValidateVersionFormat(this.Version);                       
                        break;   
                   case PropertyNames.Publisher:
                        error = ParameterizationHelper.ValidateCannotBeParameterizedBySpecification(this.Publisher);
                        break;
                   case PropertyNames.SchemaVersion:
                        error = ValidationHelper.ValidateVersionFormat(this.SchemaVersion, false);  
                        if (error == null)
                        {
                            if (!ResourceDefinition.ExpectedSchemaVersion.Equals(this.SchemaVersion, StringComparison.Ordinal))
                            {
                                error = "Supported: " + VMRoleResourceExtensionDefinition.ExpectedSchemaVersion;
                            }
                        }
                        break;                                                   
                   case PropertyNames.Type:
                        if (!ResourceDefinition.ExpectedType.Equals(this.Type, StringComparison.Ordinal))
                        {
                            error = "Supported: " + ResourceDefinition.ExpectedType;
                        }                       
                        break;             
                    case PropertyNames.Identity:
                        try
                        {
                            DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(ResourceDefinition));
                            using (MemoryStream ms = new MemoryStream())
                            {
                                ser.WriteObject(ms, this.ResourceDefinition);
                            }                          
                        }
                        catch (Exception ex)
                        {
                            error = string.Format("Unable to serialize json {0}. Exception details : {1}", this.Name ?? "Unspecified name", ex.Message);                            
                        }
                        break;
                }
                return error;
            }
        }

        public string Error { get; set; }

        public ObservableCollection<CloudServiceModelValidationResult> ValidationResults { get; private set; }

        public void UpdateValidationResults()
        {
            this.IsExpanded = true;
            ModelValidationResultsHelper.UpdateValidationResults(this, typeof(PropertyNames));
            //Add any other cross-field validations error           
        }    

        public static VmRoleResourceDefinitionViewModel CreateNewResourceDefinition(ResourceDefinitionStorageInfo resourceDefinitionStorageInfo, ObservableCollection<TreeViewItemViewModel> rootTreeViewItemViewModels)
        {
            ResourceDefinition resDef = new ResourceDefinition();
            resDef.Name = Path.GetFileNameWithoutExtension(resourceDefinitionStorageInfo.JsonFileName);
            resDef.Version = "1.0.0.0";
            resDef.SchemaVersion = ResourceDefinition.ExpectedSchemaVersion;
            resDef.Publisher = "Contoso"; //some default
            resDef.Type = ResourceDefinition.ExpectedType;
            resDef.ResourceParameters = new List<Parameter>();
            resDef.ResourceExtensionReferences = new List<ResourceExtensionReference>();
            resDef.IntrinsicSettings = new IntrinsicSettings();        
            resDef.IntrinsicSettings.HardwareProfile = new VMHardwareProfile();
            resDef.IntrinsicSettings.ScaleOutSettings = new ScaleOutSettings();
            resDef.IntrinsicSettings.ScaleOutSettings.InitialInstanceCount = "1";
            resDef.IntrinsicSettings.ScaleOutSettings.MinimumInstanceCount = "1";
            resDef.IntrinsicSettings.ScaleOutSettings.MaximumInstanceCount = "5";
            resDef.IntrinsicSettings.ScaleOutSettings.UpgradeDomainCount = "1";
            resDef.IntrinsicSettings.NetworkProfile = new VMNetworkProfile();
            resDef.IntrinsicSettings.NetworkProfile.NetworkAdapters = new List<NetworkAdapter>();
            var netAdapter = new NetworkAdapter();
            netAdapter.Name = "NIC1";
            netAdapter.IPAddresses = new List<IPAddress>();
            var ipAddress = new IPAddress
                {
                    AllocationMethod = IPAllocationMethod.Dynamic.ToString(),
                    ConfigurationName = "IPV4Configuration",
                    Type = IpProtocolVersion.IPV4.ToString()
                };
            netAdapter.IPAddresses.Add(ipAddress);
            resDef.IntrinsicSettings.NetworkProfile.NetworkAdapters.Add(netAdapter); 
            resDef.IntrinsicSettings.OperatingSystemProfile = new OperatingSystemProfile();
            resDef.IntrinsicSettings.OperatingSystemProfile.WindowsOperatingSystemProfile = new WindowsOperatingSystemProfile();

            resDef.IntrinsicSettings.StorageProfile = new VMStorageProfile();
            ViewDefinition viewDefinition = null;

            if (resourceDefinitionStorageInfo.IsResdefPkg)
            {
                viewDefinition = new ViewDefinition();
                if (resourceDefinitionStorageInfo.ViewDefFileName == null)
                {
                    string viewDefFileName = System.IO.Path.GetFileNameWithoutExtension(resourceDefinitionStorageInfo.JsonFileName) + ".viewdef";                   
                    resourceDefinitionStorageInfo.ViewDefFileName = System.IO.Path.Combine(resourceDefinitionStorageInfo.WorkingDir, viewDefFileName);
                    //resourceDefinitionStorageInfo.ViewDefFileName = System.IO.Path.Combine(resourceDefinitionStorageInfo.WorkingDir, "1.viewdef");              
                }
                viewDefinition.DefaultLanguageCode = "en-US";
                viewDefinition.Version = "1.0.0.0";
                

                viewDefinition.Sections = new List<ViewDefSection>();
                var section = new ViewDefSection();
                section.Title = ViewDefSectionViewModel.VMSettingsAutoCreatedByToolMoniker;
                viewDefinition.Sections.Add(section);
                section.Categories = new List<ViewDefCategory>();
                var category = new ViewDefCategory();
                category.CategoryName = ViewDefCategoryViewModel.VMCategoryAutoCreatedByToolMoniker;
                section.Categories.Add(category);

                 if (resourceDefinitionStorageInfo.ResourceFileNames == null)
                 {
                     resourceDefinitionStorageInfo.ResourceFileNames = new List<string>();                     
                 }
                 string resourcesFileName = System.IO.Path.Combine(resourceDefinitionStorageInfo.WorkingDir, "en-US", "1.resources");
                 resourceDefinitionStorageInfo.ResourceFileNames.Add(resourcesFileName);
            }

            VmRoleResourceDefinitionViewModel resourceDefinitionViewModel = new VmRoleResourceDefinitionViewModel(resDef, viewDefinition, resourceDefinitionStorageInfo, rootTreeViewItemViewModels, true);

            

            return resourceDefinitionViewModel;
        }       
    }
}
